﻿/******************************************************************************
 *      Class:      WeatherDataReader
 *      Authors:    Michael Slezak, Samuel Harrington
 *      
 * ----------------------------------------------------------------------------
 * 
 *      Description:
 *          This is a helper class that reads and stores a list of
 *          WeatherDataPoints from a directory of xml files
 *          
 ******************************************************************************/

////////////////////////////////////////////////////////////////////////////////
//   INCLUDES
////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;

namespace WeatherStationProgram
{
    class WeatherDataReader
    {
        private string data_dir;

        public List<WeatherDataPoint> all_points = new List<WeatherDataPoint>();
        // Year -> (Month -> (Day[])
        public SortedList<int, SortedList<int, SortedSet<int>>> point_dates = new SortedList<int, SortedList<int, SortedSet<int>>>();
        // start[]
        public SortedSet<DateTime> week_dates = new SortedSet<DateTime>();

        /// <summary>
        /// Loads the data files from the specified directory
        /// </summary>
        /// <param name="dir">the directory to read from</param>
        public void SetDirectory(string dir)
        {
            if (dir != data_dir)
            {
                data_dir = dir;
                all_points.Clear();
                readFiles();
            }
        }
        
        /// <summary>
        /// A class to compare files by name
        /// </summary>
        private class MyFileComp : IComparer<FileInfo>
        {
            public int Compare(FileInfo a, FileInfo b)
            {
                return a.Name.CompareTo(b.Name);
            }
        };

        /// <summary>
        /// Private function to read in the all the data files
        /// </summary>
        private void readFiles()
        {
            DateTime start_time = DateTime.Now;
            Console.WriteLine("{1}: Reading files from {0}", data_dir, start_time);

            if (data_dir == null)
                return;

            System.IO.DirectoryInfo info = new System.IO.DirectoryInfo(data_dir);
            if (!info.Exists)
                return;

            System.IO.FileInfo[] files = info.GetFiles("*-*.xml");
            if (files.Count() == 0)
                return;

            Array.Sort(files, new MyFileComp());

            all_points.Clear();
            point_dates.Clear();
            week_dates.Clear();

            foreach (System.IO.FileInfo file in files)
                readFile(file);

            Console.WriteLine("{0}: data loaded in {1}", DateTime.Now, DateTime.Now - start_time);
        }

        /// <summary>
        /// Read the data from the file specified
        /// </summary>
        /// <param name="file">the xml file to load</param>
        private void readFile(FileInfo file)
        {
            XmlReader reader = XmlReader.Create(file.OpenRead());

            WeatherDataPoint pt = null;
            string tag = null;

            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        tag = reader.Name;
                        if (tag == "weather")
                        {
                            pt = new WeatherDataPoint();
                            tag = null;
                        }
                        break;

                    case XmlNodeType.Text:
                        if (tag == null)
                            continue;
                        string content = reader.Value.Substring(1, reader.Value.Length - 2);
                        updatePt(pt, tag, content);
                        tag = null;
                        break;

                    case XmlNodeType.EndElement:
                        tag = null;

                        if (reader.Name != "weather")
                            continue;

                        all_points.Add(pt);

                        DateTime d = pt.date;

                        if (!point_dates.ContainsKey(d.Year))
                        {
                            SortedList<int, SortedSet<int>> yr_points = new SortedList<int, SortedSet<int>>();
                            yr_points.Add(d.Month, new SortedSet<int> { d.Day });
                            point_dates.Add(d.Year, yr_points);
                        }
                        else if (!point_dates[d.Year].ContainsKey(d.Month))
                            point_dates[d.Year].Add(d.Month, new SortedSet<int> { d.Day });
                        else
                            point_dates[d.Year][d.Month].Add(d.Day);

                        week_dates.Add(d.Date.AddDays(-(int)d.DayOfWeek));

                        pt = null;
                        break;
                }
            }
        }

        /// <summary>
        /// Fill in the point with one tag's worth of info
        /// </summary>
        /// <param name="pt">the point to store to</param>
        /// <param name="tag">the tag name</param>
        /// <param name="content">the tag contents</param>
        private void updatePt(WeatherDataPoint pt, string tag, string content)
        {
            switch (tag)
            {
                case "date":
                    string[] dparts = content.Split(new char[] { '/' });
                    int y = int.Parse(dparts[2]) + 1900;
                    if (y < 1940)
                        y += 100;
                    pt.date = new DateTime(y, int.Parse(dparts[0]), int.Parse(dparts[1]));
                    break;
                case "time":
                    string[] tparts = content.Split(new char[] { ':' });
                    int h = int.Parse(tparts[0]);
                    if (h == 12)
                        h = 0;
                    pt.date = new DateTime(pt.date.Year, pt.date.Month, pt.date.Day,
                        h + (tparts[1].Last() == 'P' ? 12 : 0),
                        int.Parse(tparts[1].Substring(0, 2)),
                        0, 0);
                    break;
                case "temperature":
                    pt.temperature = float.Parse(content);
                    break;
                case "humidity":
                    pt.humidity = float.Parse(content);
                    break;
                case "barometer":
                    pt.barometer = float.Parse(content);
                    break;
                case "windspeed":
                    pt.windSpeed = float.Parse(content);
                    break;
                case "winddirection":
                    pt.windDirection = (WeatherDataPoint.WindDirection)Enum.Parse(typeof(WeatherDataPoint.WindDirection), content);
                    break;
                case "windgust":
                    pt.windGust = float.Parse(content);
                    break;
                case "windchill":
                    pt.windChill = float.Parse(content);
                    break;
                case "heatindex":
                    pt.heatIndex = float.Parse(content);
                    break;
                case "uvindex":
                    pt.uvIndex = float.Parse(content);
                    break;
                case "rainfall":
                    pt.rainfall = float.Parse(content);
                    break;
            }
        }
    }
}
